home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / news / inn1.000 / inn1.4sec-linux-src.tar / inn / samples / inncheck < prev    next >
Text File  |  1993-03-18  |  24KB  |  1,000 lines

  1. #!/usr/bin/perl --
  2. ##  $Revision: 1.10 $
  3. ##  Sanity-check the configuration of an INN system
  4. ##  by Brendan Kehoe <brendan@cygnus.com> and Rich $alz.
  5.  
  6. $ST_MODE = 2;
  7. $ST_UID  = 4;
  8. $ST_GID  = 5;
  9.  
  10. ## =()<$newsuser = '@<NEWSUSER>@';>()=
  11. $newsuser = 'news';
  12. ## =()<$newsgroup = '@<NEWSGROUP>@';>()=
  13. $newsgroup = 'news';
  14.  
  15. ##  We use simple names, mapping them to the real filenames only when
  16. ##  we actually need a filename.
  17. %paths = (
  18. ## =()<    'active',        '@<_PATH_ACTIVE>@',>()=
  19.     'active',        '/news/lib/active',
  20. ## =()<    'archive',        '@<_PATH_ARCHIVEDIR>@',>()=
  21.     'archive',        '/news/spool/news.archive',
  22. ## =()<    'badnews',        '@<_PATH_BADNEWS>@',>()=
  23.     'badnews',        '/var/spool/rnews/bad',
  24. ## =()<    'batchdir',        '@<_PATH_BATCHDIR>@',>()=
  25.     'batchdir',        '/news/spool/out.going',
  26. ## =()<    'control.ctl',    '@<_PATH_CONTROLCTL>@',>()=
  27.     'control.ctl',    '/news/lib/control.ctl',
  28. ## =()<    'ctlprogs',        '@<_PATH_CONTROLPROGS>@',>()=
  29.     'ctlprogs',        '/news/bin/control',
  30. ## =()<    'expire.ctl',    '@<_PATH_EXPIRECTL>@',>()=
  31.     'expire.ctl',    '/news/lib/expire.ctl',
  32. ## =()<    'hosts.nntp',    '@<_PATH_INNDHOSTS>@',>()=
  33.     'hosts.nntp',    '/news/lib/hosts.nntp',
  34. ## =()<    'inews',        '@<_PATH_INEWS>@',>()=
  35.     'inews',        '/usr/local/bin/inews.nntp',
  36. ## =()<    'inn.conf',        '@<_PATH_CONFIG>@',>()=
  37.     'inn.conf',        '/news/lib/inn.conf',
  38. ## =()<    'innd',        '@<_PATH_INND>@',>()=
  39.     'innd',        '/news/bin/innd',
  40. ## =()<    'innddir',        '@<_PATH_INNDDIR>@',>()=
  41.     'innddir',        '/news/lib/innd',
  42. ## =()<    'inndstart',    '@<_PATH_INNDSTART>@',>()=
  43.     'inndstart',    '/news/bin/inndstart',
  44. ## =()<    'moderators',    '@<_PATH_MODERATORS>@',>()=
  45.     'moderators',    '/news/lib/moderators',
  46. ## =()<    'most_logs',    '@<_PATH_MOST_LOGS>@',>()=
  47.     'most_logs',    '/var/log/news',
  48. ## =()<    'newsbin',        '@<_PATH_NEWSBIN>@',>()=
  49.     'newsbin',        '/news/bin',
  50. ## =()<    'newsboot',        '@<_PATH_NEWSBOOT>@',>()=
  51.     'newsboot',        '/etc/rc.news',
  52. ## =()<    'newsfeeds',    '@<_PATH_NEWSFEEDS>@',>()=
  53.     'newsfeeds',    '/news/lib/newsfeeds',
  54. ## =()<    'overview.fmt',    '@<_PATH_SCHEMA>@',>()=
  55.     'overview.fmt',    '/news/lib/overview.fmt',
  56. ## =()<    'newslib',        '@<_PATH_NEWSLIB>@',>()=
  57.     'newslib',        '/news/lib',
  58. ## =()<    'nnrp.access',    '@<_PATH_NNRPACCESS>@',>()=
  59.     'nnrp.access',    '/news/lib/nnrp.access',
  60. ## =()<    'nnrpd',        '@<_PATH_NNRPD>@',>()=
  61.     'nnrpd',        '/news/bin/nnrpd',
  62. ## =()<    'nntpsend.ctl',    '@<_PATH_NEWSLIB>@/nntpsend.ctl',>()=
  63.     'nntpsend.ctl',    '/news/lib/nntpsend.ctl',
  64. ## =()<    'oldlogs',        '@<_PATH_MOST_LOGS>@/OLD',>()=
  65.     'oldlogs',        '/var/log/news/OLD',
  66. ## =()<    'parsectl',        '@<_PATH_PARSECTL>@',>()=
  67.     'parsectl',        '/news/bin/control/parsecontrol',
  68. ## =()<    'passwd.nntp',    '@<_PATH_NNTPPASS>@',>()=
  69.     'passwd.nntp',    '/news/lib/passwd.nntp',
  70. ## =()<    'rnews',        '@<_PATH_RNEWS>@',>()=
  71.     'rnews',        '/news/bin/rnews/rnews',
  72. ## =()<    'rnewsprogs',    '@<_PATH_RNEWSPROGS>@',>()=
  73.     'rnewsprogs',    '/news/bin/rnews',
  74. ## =()<    'spooltemp',        '@<_PATH_SPOOLTEMP>@',>()=
  75.     'spooltemp',        '/var/spool/rnews/tmp',
  76. ## =()<    'spool',        '@<_PATH_SPOOL>@',>()=
  77.     'spool',        '/news/spool',
  78. ## =()<    'spoolnews',    '@<_PATH_SPOOLNEWS>@'>()=
  79.     'spoolnews',    '/var/spool/rnews'
  80. );
  81.  
  82. ##  The sub's that check the config files.
  83. %checklist = (
  84.     'active',        'active',
  85.     'control.ctl',    'control_ctl',
  86.     'expire.ctl',    'expire_ctl',
  87.     'hosts.nntp',    'hosts_nntp',
  88.     'inn.conf',        'inn_conf',
  89.     'moderators',    'moderators',
  90.     'newsfeeds',    'newsfeeds',
  91.     'overview.fmt',    'overview_fmt',
  92.     'nnrp.access',    'nnrp_access',
  93.     'nntpsend.ctl',    'nntpsend_ctl',
  94.     'passwd.nntp',    'passwd_nntp'
  95. );
  96.  
  97. ##  The modes of the config files we can check.
  98. %modes = (
  99.     'active',        0644,
  100.     'control.ctl',    0440,
  101.     'expire.ctl',    0440,
  102.     'hosts.nntp',    0440,
  103.     'inn.conf',        0444,
  104.     'moderators',    0444,
  105.     'newsfeeds',    0444,
  106.     'overview.fmt',    0444,
  107.     'nnrp.access',    0440,
  108.     'nntpsend.ctl',    0440,
  109.     'passwd.nntp',    0440
  110. );
  111.  
  112.  
  113. sub
  114. spacious
  115. {
  116.     local ($i);
  117.  
  118.     chop;
  119.     study;
  120.     if ( /^#/ || /^$/ ) {
  121.     $i = 1;
  122.     } elsif ( /^\s/ ) {
  123.     print "$file:$line: starts with whitespace\n";
  124.     $i = 1;
  125.     } elsif ( /\s$/ ) {
  126.     print "$file:$line: ends with whitespace\n";
  127.     $i = 1;
  128.     }
  129.     $i;
  130. }
  131.  
  132. ##
  133. ##  These are the functions that verify each individual file, called
  134. ##  from the main code.  Each function gets <IN> as the open file, $line
  135. ##  as the linecount, and $file as the name of the file.
  136. ##
  137.  
  138.  
  139. ##
  140. ##  active
  141. ##
  142. sub
  143. active
  144. {
  145.     local ($group, $hi, $lo, $f, $alias, %groups, %aliases);
  146.  
  147.     input: while ( <IN> ) {
  148.     next input if &spacious($file, ++$line);
  149.     unless ( ($group, $hi, $lo, $f) = /^([^ ]+) (\d+) (\d+) (.+)$/ ) {
  150.         print "$file:$line: malformed line.\n";
  151.         next input;
  152.     }
  153.  
  154.     print "$file:$line: group `$group' already appeared\n"
  155.         if $groups{$group}++;
  156.     print "$file:$line: `$hi' <  '$lo'.\n"
  157.         if $hi < $lo && $lo != $hi + 1;
  158.  
  159.     next input if $f =~ /^[jmynx]$/;
  160.     unless ( ($alias) = $f =~ /^=(.*)$/ ) {
  161.         print "$file:$line: bad flag `$f'.\n";
  162.         next input;
  163.     }
  164.     $aliases{$alias} = $line
  165.         unless defined $groups{$alias};
  166.     }
  167.     foreach $key ( keys %aliases ) {
  168.     print "$file:$aliases{$group} aliased to unknown group `$key'.\n"
  169.         unless defined $groups{$key};
  170.     }
  171.     1;
  172. }
  173.  
  174.  
  175. ##
  176. ##  control.ctl
  177. ##
  178. %control'messages = (
  179.     'all',        1,
  180.     'checkgroups',    1,
  181.     'ihave',        1,
  182.     'newgroup',        1,
  183.     'rmgroup',        1,
  184.     'sendme',        1,
  185.     'sendsys',        1,
  186.     'senduuname',    1,
  187.     'version',        1,
  188. );
  189. %control'actions = (
  190.     'drop',        1,
  191.     'log',        1,
  192.     'mail',        1,
  193.     'doit',        1,
  194.     'doifarg',        1
  195. );
  196.  
  197. sub
  198. control_ctl
  199. {
  200.     local ($msg, $from, $ng, $act);
  201.  
  202.     input: while ( <IN> ) {
  203.     next input if &spacious($file, ++$line);
  204.  
  205.     unless ( ($msg, $from, $ng, $act) =
  206.             /^([^:]+):([^:]+):([^:]+):(.+)$/ ) {
  207.         print "$file:$line: malformed line.\n";
  208.         next input;
  209.     }
  210.     if ( !defined $control'messages{$msg} ) {
  211.         print "$file:$line: unknown control message `$msg'.\n";
  212.         next input;
  213.     }
  214.     print "$file:$line: action for unknown control messages is `doit'.\n"
  215.         if $msg eq "default" && $act eq "doit";
  216.     print "$file:$line: empty from field.\n"
  217.         if $from eq "";
  218.     print "$file:$line: bad email address.\n"
  219.         if $from ne "*" && $from !~ /[@!]/;
  220.  
  221.     ##  Perhaps check for conflicting rules, or warn about the last-match
  222.     ##  rule?  Maybe later...
  223.     print "$file:$line: may not match groups properly.\n"
  224.         if $ng ne "*" && $ng !~ /\./;
  225.     if ( $act !~ /([^=]+)(=.+)?/ ) {
  226.         print "$file:$line: malformed line.\n";
  227.         next input;
  228.     }
  229.     $act =~ s/=.*//;
  230.     print "$file:$line: unknown action `$act'\n"
  231.         if !defined $control'actions{$act};
  232.     }
  233.     1;
  234. }
  235.  
  236.  
  237. ##
  238. ##  expire.ctl
  239. ##
  240. sub
  241. expire_ctl
  242. {
  243.     local ($rem, $v, $def, $flag, $keep, $default, $purge);
  244.  
  245.     input: while ( <IN> ) {
  246.     next input if &spacious($file, ++$line);
  247.  
  248.     if ( ($v) = m@/remember/:(.+)@ ) {
  249.         print "$file:$line: more than one /remember/ line.\n"
  250.         if $rem++;
  251.         if ( $v !~ /[\d\.]+/ ) {
  252.         print "$file:$line: illegal value `$v' for remember.\n";
  253.         next input;
  254.         }
  255.         print "$file:$line: are you sure about your /remember/ value?\n"
  256.         ##  These are arbitrary "sane" values.
  257.         if $v != 0 && ($v > 60.0 || $v < 5.0);
  258.         next input;
  259.     }
  260.  
  261.     ##  Could check for conflicting lines, but that's hard.
  262.     unless ( ($pat, $flag, $keep, $default, $purge) =
  263.          /^([^:])+:([^:]+):([\d\.]+|never):([\d\.]+|never):([\d\.]+|never)$/ ) {
  264.         print "$file:$line: malformed line.\n";
  265.         next input;
  266.     }
  267.     print "$file:$line: duplicate default line\n"
  268.         if $pat eq "*" && $flag eq "a" && $def++;
  269.     print "$file:$line: unknown modflag `$flag'\n"
  270.         if $flag !~ /[mMuUaA]/;
  271.     print "$file:$line: purge `$purge' younger than default `$default'.\n"
  272.         if $purge ne "never" && $default > $purge;
  273.     print "$file:$line: default `$default' younger than keep `$keep'.\n"
  274.         if $default ne "never" && $keep ne "never" && $keep > $default;
  275.     }
  276.     1;
  277. }
  278.  
  279.  
  280. ##
  281. ##  hosts.nntp
  282. ##
  283. sub
  284. hosts_nntp
  285. {
  286.     local ($host, $pass);
  287.  
  288.     input: while ( <IN> ) {
  289.     next input if &spacious($file, ++$line);
  290.  
  291.     unless ( ($host, $pass) = /^([^:])+:(.*)$/ ) {
  292.         print "$file:$line: malformed line.\n";
  293.         next input;
  294.     }
  295.  
  296.     print "$file:$line bad format for host `$host'.\n"
  297.         unless $host =~ /^[\w\.\-]+/ || $host =~ /^(\d+\.){2}\d+/;
  298.     }
  299.     1;
  300. }
  301.  
  302.  
  303. ##
  304. ##  inn.conf
  305. ##
  306. %inn_conf'fields = (
  307.     'domain',        0,
  308.     'fromhost',        0,
  309.     'moderatormailer',    0,
  310.     'organization',    0,
  311.     'pathhost',        0,
  312.     'server',        0,
  313.     'mime-version',    0,
  314.     'mime-contenttype',    0,
  315.     'mime-encoding',    0
  316. );
  317. %inn_conf'optionals = (
  318.     'mime-version',    0,
  319.     'mime-contenttype',    0,
  320.     'mime-encoding',    0
  321. );
  322.  
  323. sub
  324. inn_conf
  325. {
  326.     local ($k, $v, $hostname, $fqdn);
  327.  
  328.     chop($hostname = `hostname`);
  329.     $fqdn = (gethostbyname($hostname))[0];
  330.     foreach $key ( keys %inn_conf'fields ) {
  331.     $inn_conf'fields{$key} = 0;
  332.     }
  333.  
  334.     input: while ( <IN> ) {
  335.     next input if &spacious($file, ++$line);
  336.  
  337.     unless ( ($k, $v) = /^(.*):\s*(.*)$/ ) {
  338.         print "$file:$line: malformed line.\n";
  339.         next input;
  340.     }
  341.     if ( !defined $inn_conf'fields{$k} ) {
  342.         print "$file:$line: Invalid field `$k'\n";
  343.         next input;
  344.     }
  345.     if ( ++$inn_conf'fields{$k} > 1 ) {
  346.         print "$file:$line: Duplicate field `$k'\n";
  347.         next input;
  348.     }
  349.     if ( $k eq "domain" ) {
  350.         print "$file:$line: domain (`$v') isn't local domain\n"
  351.         if $fqdn =~ /[^\.]+\(\..*\)/ && $v ne $1;
  352.         print "$file:$line: domain should not have a leading period\n"
  353.         if $v =~ /^\./;
  354.     } elsif ( $k eq "fromhost" ) {
  355.         print "$file:$line: fromhost isn't a valid FQDN\n"
  356.         if $v !~ /[\w\-]+\.[\w\-]+/;
  357.     } elsif ( $k eq "moderatormailer" ) {
  358.         print "$file:$line: modmailer has bad address\n"
  359.         if $v !~ /[\w\-]+\.[\w\-]+/ && $v ne "%s";
  360.     } elsif ( $k eq "organization" ) {
  361.         print "$file:$line: org is blank\n"
  362.         if $v eq "";
  363.     } elsif ( $k eq "pathhost" ) {
  364.         print "$file:$line: pathhost has a ! in it\n"
  365.         if $v =~ /!/;
  366.     } elsif ( $k eq "server" ) {
  367.         print "$file:$line: server (`$v') isn't local hostname\n"
  368.         if $pedantic && $fqdn !~ /^$v/;
  369.     }
  370.     else {
  371.         print "$file:$line: semi-known field\n";
  372.     }
  373.     }
  374.  
  375.     key: foreach $key ( keys %inn_conf'fields ) {
  376.     next key if defined $inn_conf'fields{$key};
  377.     next key if defined $inn_conf'optionals{$key};
  378.     if ( $key eq "moderatormailer" ) {
  379.         printf "$file:$line: missing $key and no moderators file.\n"
  380.         if ! -f $paths{"moderators"};
  381.     } elsif ( $pedantic ) {
  382.         printf "$file:$line: missing $key\n";
  383.     }
  384.     }
  385.     1;
  386. }
  387.  
  388.  
  389. ##
  390. ##  moderators
  391. ##
  392. sub
  393. moderators
  394. {
  395.     local ($k, $v);
  396.  
  397.     input: while ( <IN> ) {
  398.     next input if &spacious($file, ++$line);
  399.  
  400.     unless ( ($k, $v) = /^([\w\.\-\*]*):(.*)$/ ) {
  401.         print "$file:$line: malformed line.\n";
  402.         next input;
  403.     }
  404.  
  405.     if ( $k eq "" || $v eq "" ) {
  406.         print "$file:$line: missing field\n";
  407.         next input;
  408.     }
  409.     print "$file:$line: not an email address\n"
  410.         if $pedantic && $v !~ /[@!]/;
  411.     print "$file:$line: `$v' goes to local address\n"
  412.         if $pedantic && $v eq "%s";
  413.     print "$file:$line: more than one %s in address field\n"
  414.         if $v =~ /%s.*%s/;
  415.     }
  416.     1;
  417. }
  418.  
  419.  
  420. ##
  421. ##  newsfeeds
  422. ##
  423. %newsfeeds'flags = (
  424.     '<',    '^\d+$',
  425.     'A',    '^[dp]+$',
  426.     'B',    '^\d+(/\d+)?$',
  427.     'F',    '^.+$',
  428.     'G',    '^\d+$',
  429.     'H',    '^\d+$',
  430.     'I',    '^\d+$',
  431.     'N',    '^[mu]$',
  432.     'S',    '^\d+$',
  433.     'T',    '^[cflmpx]$',
  434.     'W',    '^[*nfgbmstDNHOR]*$',
  435. );
  436.  
  437. sub
  438. newsfeeds
  439. {
  440.     local ($next, $start, $me_empty, @muxes, %sites);
  441.     local ($site, $pats, $dists, $flags, $param, $type, $k, $v, $defsub);
  442.     local ($bang, $nobang, $prog);
  443.  
  444.     input: while ( <IN> ) {
  445.     $line++;
  446.     next input if /^$/;
  447.     chop;
  448.     print "$file:$line: starts with whitespace\n"
  449.         if /^\s+/;
  450.  
  451.     ##  Read continuation lines.
  452.     $start = $line;
  453.     while ( /\\$/ ) {
  454.         chop;
  455.         chop($next = <IN>);
  456.         $line++;
  457.         $next =~ s/^\s*//;
  458.         $_ .= $next;
  459.     }
  460.     next input if /^#/;
  461.     print "$file:$line: ends with whitespace\n"
  462.         if /\s+$/;
  463.  
  464.     unless ( ($site, $pats, $flags, $param) =
  465.             /^([^:]+):([^:]*):([^:]*):(.*)$/ ) {
  466.         print "$file:$line: malformed line.\n";
  467.         next input;
  468.     }
  469.  
  470.     print "$file:$line: Newsfeed `$site' has whitespace in its name\n"
  471.         if $site =~ /\s/;
  472.  
  473.     print "$file:$start: ME has exclusions\n"
  474.         if $site =~ m@^ME/@;
  475.     print "$file:$start: multiple slashes in exclusions for `$site'\n"
  476.         if $site =~ m@/.*/@;
  477.     $site =~ s@([^/]*)/.*@$1@;
  478.     print "$site, "
  479.         if $verbose;
  480.  
  481.     if ( $site eq "ME" ) {
  482.         $defsub = $pats;
  483.         $defsub =~ s@(.*)/.*@$1@;
  484.     } elsif  ( $defsub ne "" ) {
  485.         $pats = "$defsub,$pats";
  486.     }
  487.     print "$file:$start: Multiple slashes in distribution for `$site'\n"
  488.         if $pats =~ m@/.*/@;
  489.  
  490.     if ( $site eq "ME" ) {
  491.         print "$file:$start: ME flags should be empty\n"
  492.         if $flags ne "";
  493.         print "$file:$start: ME param should be empty\n"
  494.         if $param ne "";
  495.         $me_empty = 1
  496.         if $pats !~ "/.+";
  497.     }
  498.  
  499.     ##  If we don't have !junk,!control, give a helpful warning.
  500. #    if ( $site ne "ME" && $pats =~ /!\*,/ ) {
  501. #        print "$file:$start: consider adding !junk to $site\n"
  502. #        if $pats !~ /!junk/;
  503. #        print "$file:$start: consider adding !control to $site\n"
  504. #        if $pats !~ /!control/;
  505. #    }
  506.  
  507.     ##  Check distributions.
  508.     if ( ($dists) = $pats =~ m@.*/(.*)@ ) {
  509.         $bang = $nobang = 0;
  510.         dist: foreach $d ( split(/,/, $dists) ) {
  511.         if ( $d =~ /^!/ ) {
  512.             $bang++;
  513.         }
  514.         else {
  515.             $nobang++;
  516.         }
  517.         print "$file:$start: questionable distribution `$d'\n"
  518.             if $d !~ /^!?[a-z0-9-]+$/;
  519.         }
  520.         print "$file:$start: both ! and non-! distributions\n"
  521.         if $bang && $nobang;
  522.     }
  523.     $type = "f";
  524.     flag: foreach $flag ( split(/,/, $flags) ) {
  525.         ($k, $v) = $flag =~ /(.)(.*)/;
  526.         if ( !defined $newsfeeds'flags{$k} ) {
  527.         print "$file:$start: unknown flag `$flag'\n";
  528.         next flag;
  529.         }
  530.         if ( $v !~ /$newsfeeds'flags{$k}/ ) {
  531.         print "$file:$start: bad value `$v' for flag `$k'\n";
  532.         next flag;
  533.         }
  534.         $type = $v
  535.         if $k eq "T";
  536.     }
  537.  
  538.     ##  Warn about multiple feeds.
  539.     if ( !defined $sites{$site} ) {
  540.         $sites{$site} = $type;
  541.     } elsif ( $sites{$site} ne $type ) {
  542.         print "$file:$start: feed $site multiple conflicting feeds\n";
  543.     }
  544.  
  545.     if ( $type =~ /[cpx]/ ) {
  546.         $prog = $param;
  547.         $prog =~ s/\s.*//;
  548.         print "$file:$start: relative path for $site\n"
  549.         if $prog !~ m@^/@;
  550.         print "$file:$start: `$prog' is not executable for $site\n"
  551.         if ! -x $prog;
  552.     }
  553.  
  554.     ##  If multiplex target not known, add to multiplex list.
  555.     push(@muxes, "$start: undefined multiplex `$param'")
  556.         if $type eq "m" && !defined $sites{$param};
  557.     }
  558.  
  559.     ##  Go through and make sure all referenced multiplex exist.
  560.     foreach (@muxes) {
  561.     print "$file:$_\n"
  562.         if /`(.*)'/ && !defined $sites{$1};
  563.     }
  564.     print "$file:0: ME entry accepts all incoming article distributions\n"
  565.     if !defined $sites{"ME"} || $me_empty;
  566.  
  567.     print "done.\n"
  568.     if $verbose;
  569.     1;
  570. }
  571.  
  572.  
  573. ##
  574. ##  overview.fmt
  575. ##
  576. %overview_fmtheaders = (
  577.     'Approved',        1,
  578.     'Bytes',        1,
  579.     'Control',        1,
  580.     'Date',        1,
  581.     'Distribution',    1,
  582.     'Expires',        1,
  583.     'From',        1,
  584.     'Lines',        1,
  585.     'Message-ID',    1,
  586.     'Newsgroups',    1,
  587.     'Path',        1,
  588.     'References',    1,
  589.     'Reply-To',        1,
  590.     'Sender',        1,
  591.     'Subject',        1,
  592.     'Supersedes',    1,
  593. );
  594.  
  595. sub
  596. overview_fmt
  597. {
  598.     local ($header, $mode, $sawfull);
  599.  
  600.     $sawfull = 0;
  601.     input: while ( <IN> ) {
  602.     next input if &spacious($file, ++$line);
  603.  
  604.     unless ( ($header, $mode) = /^([^:]+):([^:]*)$/ ) {
  605.         print "$file:$line: malformed line.\n";
  606.         next input;
  607.     }
  608.  
  609.     #print "$file:$line: unknown header `$header'\n"
  610.     #    if !defined $overview_fmtheaders{$header};
  611.     if ( $mode eq "full" ) {
  612.         $sawfull++;
  613.     } elsif ( $mode eq "" ) {
  614.         print "$file:$line: short header `$header' appears after full one\n"
  615.         if $sawfull;
  616.     } else {
  617.         print "$file:$line: unknown mode `$mode'\n";
  618.     }
  619.     }
  620.     1;
  621. }
  622.  
  623.  
  624. ##
  625. ##  nnrp.access
  626. ##
  627. sub
  628. nnrp_access
  629. {
  630.     local ($host, $perm, $user, $pass, $groups);
  631.  
  632.     input: while ( <IN> ) {
  633.     next input if &spacious($file, ++$line);
  634.  
  635.     unless ( ($host, $perm, $user, $pass, $groups) =
  636.         /^([^:])+:([^:]*):([^:]*):([^:]*):([^:]+)$/ ) {
  637.         print "$file:$line: malformed line.\n";
  638.         next input;
  639.     }
  640.  
  641.     print "$file:$line: access list has a / in it\n"
  642.         if $host =~ m@/@;
  643.     print "$file:$line: unknown permissions: `$perm'\n"
  644.         unless $perm eq "" || $perm =~ /[RP]/;
  645.     }
  646.     1;
  647. }
  648.  
  649.  
  650. ##
  651. ##  nntpsend.ctl
  652. ##
  653. sub
  654. nntpsend_ctl
  655. {
  656.     local ($site, $fqdn, $flags, $f, $v);
  657.  
  658.     input: while ( <IN> ) {
  659.     next input if &spacious($file, ++$line);
  660.  
  661.     ##  Ignore the size info for now.
  662.     unless ( ($site, $fqdn, $flags) =
  663.             /^([\w\-\.]+):([^:]*):[^:]*:([^:]*)$/ ) {
  664.         print "$file:$line: malformed line.\n";
  665.         next input;
  666.     }
  667.     print "$file:$line: FQDN is empty for `$site'\n"
  668.         if $fqdn eq "";
  669.  
  670.     next input if $flags eq "";
  671.     flag: foreach (split(/ /, $flags)) {
  672.         unless ( ($f, $v) = /^-([adrtTpS])(.*)$/ ) {
  673.         print "$file:$line: unknown argument for `$site'\n";
  674.         next flag;
  675.         }
  676.         print "$file:$line: unknown argument to option `$f': $flags\n"
  677.         if ( $f eq "t" || $f eq "T" ) && $v !~ /\d+/;
  678.     }
  679.     }
  680.     1;
  681. }
  682.  
  683.  
  684. ##
  685. ##  passwd.nntp
  686. ##
  687. sub
  688. passwd_nntp
  689. {
  690.     local ($name, $pass);
  691.  
  692.     input: while ( <IN> ) {
  693.     next input if &spacious($file, ++$line);
  694.  
  695.     unless ( ($name, $pass) = /[\w\-\.]+:(.*):(.*)(:authinfo)?$/ ) {
  696.         next input;
  697.         print "$file:$line: malformed line.\n";
  698.     }
  699.     print "$file:$line: username/password must both be blank or non-blank\n"
  700.         if ( $name eq "" && $pass ne "" ) || ($name ne "" && $pass eq "");
  701.     }
  702.     1;
  703. }
  704.  
  705.  
  706. ##
  707. ##  Routines to check permissions
  708. ##
  709.  
  710. ##  Given a file F, check its mode to be M, and its ownership to be by the
  711. ##  user U in the group G.  U and G have defaults.
  712. sub
  713. checkperm
  714. {
  715.     local ($f, $m, $u, $g) = ( @_, $newsuser, $newsgroup);
  716.     local (@sb, $owner, $group, $mode);
  717.  
  718.     die "Internal error, undefined name in perm from ", (caller(0))[2], "\n"
  719.     if !defined $f;
  720.     die "Internal error, undefined mode in perm from ", (caller(0))[2], "\n"
  721.     if !defined $m;
  722.  
  723.     if ( ! -e $f ) {
  724.     print "$pfx$f:0: missing\n";
  725.     }
  726.     else {
  727.     @sb = stat _;
  728.     $owner = (getpwuid(@sb[$ST_UID]))[0];
  729.     $group = (getgrgid(@sb[$ST_GID]))[0];
  730.     $mode  = @sb[$ST_MODE] & ~0770000;
  731.  
  732.     ##  Ignore setgid bit on directories.
  733.     $mode &= ~0777000
  734.         if -d _;
  735.  
  736.     if ( $owner ne $u ) {
  737.         print "$pfx$f:0: owned by $owner, should be $u\n";
  738.         print "chown $u $f\n"
  739.         if $fix;
  740.     }
  741.     if ( $group ne $g ) {
  742.         print "$pfx$f:0: in group $group, should be $g\n";
  743.         print "chgrp $g $f\n"
  744.         if $fix;
  745.     }
  746.     if ( $mode ne $m ) {
  747.         printf "$pfx$f:0: mode %o, should be %o\n", $mode, $m;
  748.         printf "chmod %o $f\n", $m
  749.         if $fix;
  750.     }
  751.     }
  752. }
  753.  
  754. ##  Return 1 if the Intersection of the files in the DIR and FILES is empty.
  755. ##  Otherwise, report an error for each illegal file, and return 0.
  756. sub
  757. intersect
  758. {
  759.     local ($dir, @files) = @_;
  760.     local (@in, %dummy, $i);
  761.  
  762.     if ( !opendir(DH, $dir) ) {
  763.     print "$pfx$dir:0: can't open directory\n";
  764.     }
  765.     else {
  766.     @in = grep($_ ne "." && $_ ne "..", readdir(DH));
  767.     closedir(DH);
  768.     }
  769.  
  770.     $i = 1;
  771.     if ( scalar(@in) ) {
  772.     foreach ( @files ) {
  773.         $dummy{$_}++;
  774.     }
  775.     foreach ( grep ($dummy{$_} == 0, @in) ) {
  776.         print "$pfx$dir:0: ERROR: illegal file `$_' in directory\n";
  777.         $i = 0;
  778.     }
  779.     }
  780.     $i;
  781. }
  782.  
  783. @directories = (
  784.     'archive', 'badnews', 'batchdir', 'ctlprogs', 'most_logs', 'newsbin',
  785.     'newslib', 'oldlogs', 'rnewsprogs', 'spooltemp', 'spool', 'spoolnews'
  786. );
  787. @control_scripts = (
  788.     'checkgroups', 'default', 'docheckgroups', 'ihave', 'newgroup', 'rmgroup',
  789.     'sendme', 'sendsys', 'senduuname', 'version'
  790. );
  791. @rnews_programs = (
  792.     'c7unbatch', 'decode', 'encode'
  793. );
  794. @newsbin_public = (
  795.     'archive', 'batcher', 'buffchan', 'convdate', 'cvtbatch', 'expire',
  796.     'filechan', 'getlist', 'grephistory', 'innconfval', 'innxmit',
  797.     'makeactive', 'makehistory', 'newsrequeue', 'nntpget', 'overchan',
  798.     'prunehistory', 'shlock', 'shrinkfile'
  799. );
  800. @newsbin_private = (
  801.     'ctlinnd', 'ctlrun', 'expirerm', 'inncheck', 'innstat', 'innwatch',
  802.     'makegroup', 'news.daily', 'nntpsend', 'scanlogs', 'sendbatch',
  803.     'tally.control', 'tally.unwanted', 'updatemods', 'writelog'
  804. );
  805. @newslib_private = (
  806.     'send-ihave', 'send-nntp', 'send-uucp'
  807. );
  808. @newslib_private_read = (
  809.     'innlog.awk'
  810. );
  811.  
  812. ## The modes for the various programs.
  813. %prog_modes = (
  814.     'inews',        02555,
  815.     'innd',         0555,
  816.     'newsboot',         0550,
  817.     'nnrpd',         0555,
  818.     'parsectl',         0550,
  819.     'rnews',        02555
  820. );
  821.  
  822. ##  Check the permissions of nearly every file in an INN installation.
  823. sub
  824. check_all_perms
  825. {
  826.     local ($rnewsprogs) = $paths{'rnewsprogs'};
  827.     local ($ctlprogs) = $paths{'ctlprogs'};
  828.     local ($newsbin) = $paths{'newsbin'};
  829.     local ($newslib) = $paths{'newslib'};
  830.  
  831.     foreach ( @directories ) {
  832.     &checkperm($paths{$_}, 0775);
  833.     }
  834.     &checkperm($paths{'innddir'}, 0770);
  835.     foreach ( keys %prog_modes ) {
  836.     &checkperm($paths{$_}, $prog_modes{$_});
  837.     }
  838.     &checkperm($paths{'inndstart'}, 0555, 'root', 'bin');
  839.     foreach ( keys %paths ) {
  840.     &checkperm($paths{$_}, $modes{$_})
  841.         if defined $modes{$_};
  842.     }
  843.     foreach ( @newslib_private ) {
  844.     &checkperm("$newslib/$_", 0550);
  845.     }
  846.     foreach ( @newslib_private_read ) {
  847.     &checkperm("$newslib/$_", 0440);
  848.     }
  849.     foreach ( @newsbin_private ) {
  850.     &checkperm("$newsbin/$_", 0550);
  851.     }
  852.     foreach ( @newsbin_public ) {
  853.     &checkperm("$newsbin/$_", 0555);
  854.     }
  855.     foreach ( @control_scripts ) {
  856.     &checkperm("$ctlprogs/$_", 0550);
  857.     }
  858.     foreach ( @rnews_programs ) {
  859.     &checkperm("$rnewsprogs/$_", 0555);
  860.     }
  861.  
  862.     ##  Also make sure that @rnews_programs are the *only* programs in there;
  863.     ##  anything else is probably someone trying to spoof rnews into being bad.
  864.     &intersect($rnewsprogs, @rnews_programs);
  865.  
  866.     1;
  867. }
  868.  
  869.  
  870. ##
  871. ##  Parsing, main routine.
  872. ##
  873.  
  874. sub
  875. Usage
  876. {
  877.     local ($i) = 0;
  878.  
  879.     print "Usage error: @_.\n";
  880.     print
  881. "Usage:
  882.     $program [-v] [-noperm] [-pedantic] [-perms [-fix] ] [-a|file...]
  883. File to check may be followed by \"=path\" to use the specified path.  All
  884. files are checked if -a is used or if -perms is not used.  Files that may
  885. be checked are:\n";
  886.     foreach ( sort(keys %checklist) ) {
  887.     printf "     %-20s", $_;
  888.     if ( ++$i == 3) {
  889.         print "\n";
  890.         $i = 0;
  891.     }
  892.     }
  893.     print "\n"
  894.     if $i;
  895.     exit 0;
  896. }
  897.  
  898.  
  899. sub
  900. parse_flags
  901. {
  902.     $all = 0;
  903.     $fix = 0;
  904.     $perms = 0;
  905.     $noperms = 0;
  906.     $verbose = 0;
  907.     @todo = ();
  908.  
  909.     arg: foreach ( @ARGV ) {
  910.     if ( /-a/ ) {
  911.         $all++;
  912.         next arg;
  913.     }
  914.     if ( /^-v/ ) {
  915.         $verbose++;
  916.         next arg;
  917.     }
  918.     if ( /^-ped/ ) {
  919.         $pedantic++;
  920.         next arg;
  921.     }
  922.     if ( /^-f/ ) {
  923.         $fix++;
  924.         next arg;
  925.     }
  926.     if ( /^-per/ ) {
  927.         $perms++;
  928.         next arg;
  929.     }
  930.     if ( /^-noperm/ ) {
  931.         $noperms++;
  932.         next arg;
  933.     }
  934.     if ( /^-/ ) {
  935.         &Usage("Unknown flag `$_'");
  936.     }
  937.     if ( ($k, $v) = /(.*)=(.*)/ ) {
  938.         &Usage("Can't check `$k'")
  939.         if !defined $checklist{$k};
  940.         push(@todo, $k);
  941.         $paths{$k} = $v;
  942.         next arg;
  943.     }
  944.     &Usage("Can't check `$_'")
  945.         if !defined $checklist{$_};
  946.     push(@todo, $_);
  947.     }
  948.  
  949.     &Usage("Can't use `-fix' without `-perm'")
  950.     if $fix && !$perms;
  951.     &Usage("Can't use `-noperm' with `-perm'")
  952.     if $noperms && $perms;
  953.     $pfx = $fix ? '# ' : '';
  954.  
  955.     @todo = grep(defined $checklist{$_}, sort(keys %paths))
  956.     if $all || (scalar(@todo) == 0 && ! $perms);
  957. }
  958.  
  959.  
  960. $program = $0;
  961. $program =~ s@.*/@@;
  962. $| = 1;
  963. &parse_flags();
  964. action: foreach $workfile ( @todo ) {
  965.     $file = $paths{$workfile};
  966.     if ( ! -f $file ) {
  967.     print "$file:0: file missing\n";
  968.     next action;
  969.     }
  970.     print "Looking at $file...\n"
  971.     if $verbose;
  972.     if ( !open(IN, $file) ) {
  973.     print "$pfx$workfile:0: can't open $!\n";
  974.     next action;
  975.     }
  976.     &checkperm($file, $modes{$workfile})
  977.     if $noperms == 0 && !$perms && defined $modes{$workfile};
  978.     $line = 0;
  979.     eval "&$checklist{$workfile}" || warn "$@";
  980.     close(IN);
  981. }
  982.  
  983. &check_all_perms()
  984.     if $perms;
  985. exit(0);
  986.  
  987. if ( 0 ) {
  988.     &active();
  989.     &control_ctl();
  990.     &hosts_nntp();
  991.     &expire_ctl();
  992.     &inn_conf();
  993.     &moderators();
  994.     &nntpsend_ctl();
  995.     &nnrp_access();
  996.     &newsfeeds();
  997.     &overview_fmt();
  998.     &passwd_nntp();
  999. }
  1000.